package main

import (
	"fmt"
	"unsafe"
)

// 很像namedtuple 都是定义列的组合
type Course struct {
	name  string
	Price int
	url   string
}

// 通过(c Course) 把方法绑定到结构体，达到封装方法的效果
func (c Course) printCourseInfo() {
	// fmt.Print("课程名字:", c.name)
	fmt.Printf("课程名字:%s,课程价格%d,课程网址：%s \n", c.name, c.Price, c.url)
}

// 结构体和方法只能在同一包内
func (c Course) setPrice(print int) {
	c.Price = print
}
func main() {
	// go 语言不支持面向对象
	// 面向对象三个基本特征 1 封装 2 继承 3 多态 【4 方法重载 5 抽象基类】
	// go 语言中的struct更像python中的namedtuple
	// go 语言中没有class这个概念，

	// 1.实例化结构体赋值 kv形式
	var c Course = Course{
		name:  "django",
		Price: 100,
		url:   "https://imocc.com",
	}

	// 访问
	fmt.Println(c.name, c.Price, c.url)
	// go语言中首字母大小写 控制可见性，想在定义保外访问需要首字母大写

	// 2.第二种实例化方式
	c2 := Course{
		"scrapy",
		200,
		"www.imooc.com",
	}
	// fmt.Println(c2.name, c2.price, c2.url)
	// 本质是
	Course.setPrice(c2, 300) // 所以因为结构体是值传递，固此处的价格是改不了的，如何解决 所以只能绑定时需要绑定 指针应该改为一下形式
	// func (c *Course) setPrice(print int) {
	// 	c.price = print
	// }
	c2.setPrice(300) //这种形式实际是以上形式的语法糖，
	c2.printCourseInfo()

	// 3. 通过一个指向结构体的指针访问,获取对象的值
	c3 := &Course{"tornado", 100, "http://www.ab.com"}
	fmt.Printf("%T\n", c3)
	fmt.Println((*c3).name, (*c3).Price, (*c3).url) // 正常应该通过指针访问对象
	// 另一个根本原因是因为go语言的指针是受限的，不能偏移，不能运算，不能像c一样灵活，所以可以猜测出意图
	fmt.Println(c3.name, c3.Price, c3.url) // 这里是go语言的语法糖，go发现c3是指针的话会进行转换 把c3转为(*c3)

	// 4. 零值
	c4 := Course{}
	fmt.Println(c4.name) // 没有赋值会输出该字段的默认类型值

	// 5. 多种方式零值初始化结构体

	var c5 Course = Course{}
	var c6 Course
	var c7 *Course = new(Course) // 或者 &Course{}
	//var c8 *Course
	// 为什么c6和c8 表现出来的结果不一样，指针如果只申明不赋值，默认值是 nil 没有内存地址 c6不是指针是结构体类型
	fmt.Println("零初始化")
	fmt.Println(c5.Price)
	fmt.Println(c6.Price)
	fmt.Println(c7.Price)
	// fmt.Println(c8.price) // 此处会报错

	// 6.结构体是值类型
	c9 := Course{"scrapy", 200, "www.imooc.com"}
	c10 := c9
	fmt.Println(c9)
	fmt.Println(c10)
	c9.Price = 300 // 只会改变c9中的price c10 不会变因为是值类型 相当于重新复制了一份跟原数据已经没有关系了
	fmt.Println(c9)
	fmt.Println(c10)

	// go 语言中struct无处不在
	// 7. 结构体占用内存的大小 可以使用sizeof来查看对象占用内存的大小

	fmt.Println(unsafe.Sizeof(1))
	// 此处无论后面字符多少输出都是 16 因为 go中string的本质是一个结构体 如下
	// type string struct {
	// 	Data uintptr // 指针占了8个长度
	// 	Len  int     // 长度64位系统占了8个长度
	// }
	fmt.Println(unsafe.Sizeof("scrapy"))

	// 8. slice的数据结构大小
	// type slice struct {
	// 	array unsafe.Pointer // 底层数组的地址
	// 	len   int            // 长度
	// 	cap   int            // 容量
	// }
	s1 := []string{"django111", "scrapy"}
	fmt.Println("切片结构占用内存", unsafe.Sizeof(s1))

	m1 := map[string]string{
		"name1": "django",
		"name2": "scrapy",
		"name3": "django",
	}
	fmt.Println("map结构占用内存", unsafe.Sizeof(m1))

	// 结构体方法 代码在头部

	// go 语言不支持继承，但有方法达到效果 组合 代码在compose目录
}
